package com.my.utils;

import com.my.advice.ResponseDevice;
import com.my.annotation.*;
import com.my.annotation.http.GET;
import com.my.annotation.http.Handler;
import com.my.annotation.http.POST;
import com.my.config.GlobalConfig;
import com.my.factory.HandlerBeanFactory;
import com.my.middleware.BeforeFilter;
import com.my.middleware.MiddlewareActuator;
import com.my.model.HandlerDetails;
import com.my.model.RequestMap;
import com.my.resquest.NettyHttpRequest;
import com.my.trie.TreeNode;
import com.my.websocket.WebSocketHandler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;


public class AnnotationUtils {


    private static final String GET = "GET";

    private static final String POST = "POST";

    /**
     * GET方法路由树
     */
    private static final TreeNode GET_TREENODE = new TreeNode("/", new ArrayList<>());

    /**
     * POST方法路由树
     */
    private static final TreeNode POST_TREENODE = new TreeNode("/", new ArrayList<>());

    private static final List<String> PATHS = new ArrayList<>();

    private static MiddlewareActuator middlewareActuator;

    private static ResponseDevice responseDevice;

    private static final List<WebSocketHandler> webSocketHandlers = new ArrayList<>();


    /**
     * 异常处理器 享元池
     */
    private static final Map<String, Map<Method, Object>> exceptionHandlers = new HashMap<>();


    public static void init(Class<?> clazz, GlobalConfig globalConfig) {
        PrintUtil.info("router init.....");
        List<Class<?>> allClass = ClassUtil.getClassList(clazz.getPackage().getName(), Boolean.TRUE);
        try {
            buildHttpHandler(allClass);
            middlewareActuator = new MiddlewareActuator();
            //加载中间件
            buildMiddleware(allClass, middlewareActuator);
            //加载返回值包装器
            buildResponseDevice(allClass);
            //构建统一异常处理
            buildControllerExceptionDevice(allClass);
            if (globalConfig.getWebSocket().getOpen()) {
                buildWebSocketHandler(allClass);
                PrintUtil.printPath("websocket " + globalConfig.getWebSocket().getUrl());
            }
            PATHS.forEach(PrintUtil::printPath);
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param allClass 扫描的所有类
     */
    public static void buildHttpHandler(List<Class<?>> allClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //常规路径
        List<Method> ordinaryPaths = new ArrayList<>();
        //动态路径
        List<Method> dynamicPaths = new ArrayList<>();
        //sort
        for (Class<?> aClass : allClass) {
            Handler handlerAnnotation = aClass.getAnnotation(Handler.class);
            if (null == handlerAnnotation) {
                continue;
            }
            for (Method method : aClass.getMethods()) {
                POST postAnnotation = method.getAnnotation(POST.class);
                if (null != postAnnotation) {
                    if (postAnnotation.value().contains("{") && postAnnotation.value().contains("}")) {
                        dynamicPaths.add(method);
                    } else {
                        ordinaryPaths.add(method);
                    }
                }
                GET getAnnotation = method.getAnnotation(GET.class);
                if (null != getAnnotation) {
                    if (getAnnotation.value().contains("{") && getAnnotation.value().contains("}")) {
                        dynamicPaths.add(method);
                    } else {
                        ordinaryPaths.add(method);
                    }
                }
            }
        }
        ordinaryPaths.addAll(dynamicPaths);
        ordinaryPaths = ordinaryPaths.stream().distinct().collect(Collectors.toList());
        for (Method method : ordinaryPaths) {
            Class<?> declaringClass = method.getDeclaringClass();
            Handler handlerAnnotation = declaringClass.getAnnotation(Handler.class);
            String groupPath = handlerAnnotation.value();
            POST postAnnotation = method.getAnnotation(POST.class);
            if (postAnnotation != null) {
                if (!checkMethod(method)) {
                    continue;
                }
                HandlerDetails handlerDetails = new HandlerDetails();
                handlerDetails.setPath(pathJoin(groupPath, postAnnotation.value()));
                handlerDetails.setContentType(postAnnotation.contentType());
                handlerDetails.setInstance(HandlerBeanFactory.getBean(declaringClass));
                handlerDetails.setMethod(method);
                handlerDetails.setPathVariable(isExistPathVariable(handlerAnnotation.value()));
                checkRepeatPath(POST, handlerDetails.getPath());
                POST_TREENODE.putPath(handlerDetails.getPath(), handlerDetails);
                PATHS.add(POST + " " + handlerDetails.getPath());
            }
            GET getAnnotation = method.getAnnotation(GET.class);
            if (getAnnotation != null) {
                if (!checkMethod(method)) {
                    continue;
                }
                HandlerDetails handlerDetails = new HandlerDetails();
                handlerDetails.setPath(pathJoin(groupPath, getAnnotation.value()));
                handlerDetails.setContentType(getAnnotation.contentType());
                handlerDetails.setInstance(HandlerBeanFactory.getBean(declaringClass));
                handlerDetails.setMethod(method);
                handlerDetails.setPathVariable(isExistPathVariable(handlerAnnotation.value()));
                checkRepeatPath(GET, handlerDetails.getPath());
                GET_TREENODE.putPath(handlerDetails.getPath(), handlerDetails);
                PATHS.add(GET + " " + handlerDetails.getPath());
            }
        }
    }

    /**
     * 判断是否为404请求
     *
     * @param path 路径
     * @return true = 404
     */
    public static Boolean check404(String path) {
        TreeNode get = GET_TREENODE.getPath(path);
        TreeNode post = POST_TREENODE.getPath(path);
        if ((get == null || !get.getEnd()) && (post == null || !post.getEnd())) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    /**
     * 检查是否为405 请求
     *
     * @param path   url
     * @param method POST/GET
     * @return true = 405
     */
    public static Boolean check405(String path, String method) {
        if (GET.equals(method)) {
            TreeNode post = POST_TREENODE.getPath(path);
            TreeNode get = GET_TREENODE.getPath(path);
            if (post != null && post.getEnd() && null == get) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
        if (POST.equals(method)) {
            TreeNode post = POST_TREENODE.getPath(path);
            TreeNode get = GET_TREENODE.getPath(path);
            if (get != null && get.getEnd() && null == post) {
                return Boolean.TRUE;
            }
            return Boolean.FALSE;
        }
        throw new RuntimeException("method not found");
    }


    /**
     * 执行方法
     *
     * @param url  浏览器请求路径
     * @param args 参数列表
     * @return 执行结果
     */
    public static Object executeRequest(String url, String method, Object[] args) {
        HandlerDetails handlerDetails = null;
        TreeNode treeNode = null;
        if (GET.equals(method)) {
            treeNode = GET_TREENODE.getPath(url);
            if (treeNode == null) {
                throw new RuntimeException("url not found");
            }
            handlerDetails = treeNode.getHandlerDetails();
        }

        if (POST.equals(method)) {
            treeNode = POST_TREENODE.getPath(url);
            if (treeNode == null) {
                throw new RuntimeException("url not found");
            }
            handlerDetails = treeNode.getHandlerDetails();
        }

        if (null == handlerDetails) {
            throw new RuntimeException("url not found");
        }
        NettyHttpRequest request = (NettyHttpRequest) args[0];
        //解析路径参数
        parsePathVariable(url, request, handlerDetails.getPath());
        Object result;
        try {
            result = handlerDetails.getMethod().invoke(handlerDetails.getInstance(), args);
        } catch (IllegalAccessException | InvocationTargetException e) {
            if (e instanceof InvocationTargetException) {
                throw new RuntimeException(((InvocationTargetException) e).getTargetException());
            }
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 检查请求方法参数列表是否符合规范
     *
     * @param method 方法
     * @return true合格
     */
    private static Boolean checkMethod(Method method) {
        Parameter[] parameters = method.getParameters();
        if (parameters.length == 0 || !parameters[0].getType().equals(NettyHttpRequest.class)) {
            PrintUtil.warn(String.format("错误方法声明 %s", method.getDeclaringClass().getName() + "." + method.getName()));
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    /**
     * 拼接url
     *
     * @param groupPath 组 url
     * @param path      api 路径
     * @return 请求url
     */
    private static String pathJoin(String groupPath, String path) {
        if (!groupPath.isEmpty()) {
            if (!groupPath.startsWith("/")) {
                groupPath = "/" + groupPath;
            }
            if (groupPath.endsWith("/")) {
                groupPath = groupPath.substring(0, groupPath.length() - 1);
            }
        }
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        return groupPath + path;
    }


    /**
     * 重复路径校验
     *
     * @param method 请求方法
     * @param path   请求url
     */
    private static void checkRepeatPath(String method, String path) {
        if (POST.equals(method)) {
            TreeNode treeNode = POST_TREENODE.getPath(path);
            if (treeNode != null && treeNode.getEnd()) {
                PrintUtil.error("POST path contains repeat path : " + path);
                System.exit(-1);
            }
        }
        if (GET.equals(method)) {
            TreeNode treeNode = GET_TREENODE.getPath(path);
            if (treeNode != null && treeNode.getEnd()) {
                PrintUtil.error("GET path contains repeat path : " + path);
                System.exit(-1);
            }
        }
    }

    /**
     * 是否存在路径参数的情况
     */
    public static String isExistPathVariable(String path) {
        StringBuilder sb = new StringBuilder();
        if (path.contains("/{")) {
            for (String s : path.split("/\\{")) {
                if (sb.length() > 0) {
                    sb.append(",");
                }
                sb.append(s, 0, s.length() - 1);
            }
        }
        return sb.toString();
    }

    /**
     * 解析 动态路由参数
     *
     * @param url     url
     * @param request 请求包装类
     * @param webUrl  web浏览器请求路径
     */
    private static void parsePathVariable(String url, NettyHttpRequest request, String webUrl) {
        if (webUrl.contains("{") && webUrl.contains("}")) {
            String[] names = webUrl.split("/");
            String[] split = url.split("/");
            RequestMap data = new RequestMap();
            for (int i = 0; i < names.length; i++) {
                String thisName = names[i];
                if (thisName.startsWith("{") && thisName.endsWith("}")) {
                    String param = thisName.substring(1, thisName.length() - 1);
                    data.put(param, split[i]);
                }
            }
            request.setPathVariable(data);
        }
    }


    /**
     * 构建 中间件
     *
     * @param allClass
     */
    private static void buildMiddleware(List<Class<?>> allClass, MiddlewareActuator middlewareActuator) throws InstantiationException, IllegalAccessException {
        for (Class<?> aClass : allClass) {
            boolean assignableFrom = BeforeFilter.class.isAssignableFrom(aClass);
            if (!assignableFrom) {
                continue;
            }
            Before beforeAnnotation = aClass.getAnnotation(Before.class);
            if (null != beforeAnnotation) {
                Object instance = aClass.newInstance();
                middlewareActuator.useBefore((BeforeFilter) instance);
            }
        }
    }

    /**
     * 构建 统一返回值包装器
     *
     * @param allClass
     */
    private static void buildResponseDevice(List<Class<?>> allClass) throws InstantiationException, IllegalAccessException {
        for (Class<?> aClass : allClass) {
            boolean assignableFrom = ResponseDevice.class.isAssignableFrom(aClass);
            if (!assignableFrom) {
                continue;
            }
            ResponseHandler responseHandler = aClass.getAnnotation(ResponseHandler.class);
            if (null != responseHandler) {
                //返回值处理器 只支持一个实例
                Object instance = aClass.newInstance();
                responseDevice = (ResponseDevice) instance;
                return;
            }
        }
    }

    /**
     * 构建 统一异常返回处理器
     *
     * @param allClass
     */
    private static void buildControllerExceptionDevice(List<Class<?>> allClass) throws InstantiationException, IllegalAccessException {
        for (Class<?> aClass : allClass) {
            ControllerExceptionAdvice exceptionAdvice = aClass.getAnnotation(ControllerExceptionAdvice.class);
            if (null != exceptionAdvice) {
                Object instance = aClass.newInstance();
                for (Method method : aClass.getMethods()) {
                    ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class);
                    if (exceptionHandler != null) {
                        if (method.getReturnType().equals(Void.TYPE)) {
                            PrintUtil.error("错误方法声明: " + aClass.getName() + "." + method.getName());
                            System.exit(-1);
                        }
                        Map<Method, Object> data = new HashMap<>(1);
                        data.put(method, instance);
                        if (null == exceptionHandler.value()) {
                            exceptionHandlers.put(Exception.class.getName(), data);
                        } else {
                            exceptionHandlers.put(exceptionHandler.value().getName(), data);
                        }
                    }
                }
            }
        }
    }

    /**
     * 构建 websocket 处理器
     *
     * @param allClass
     */
    private static void buildWebSocketHandler(List<Class<?>> allClass) {
        Map<Object, Integer> data = new HashMap<>();
        for (Class<?> aClass : allClass) {
            boolean assignableFrom = WebSocketHandler.class.isAssignableFrom(aClass);
            if (!assignableFrom) {
                continue;
            }
            if (!WebSocketHandler.class.getName().equals(aClass.getName())) {
                try {
                    int value = 0;
                    Order order = aClass.getAnnotation(Order.class);
                    if (order != null) {
                        value = order.value();
                    }
                    //非抽象类进行创建
                    if (aClass.getModifiers() != Modifier.PUBLIC) {
                        continue;
                    }
                    data.put(aClass.newInstance(), value);
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        //sort
        List<Map.Entry<Object, Integer>> list = new ArrayList<>(data.entrySet());
        //然后通过比较器来实现排序
        list.sort(new Comparator<Map.Entry<Object, Integer>>() {
            //升序排序
            public int compare(Map.Entry<Object, Integer> o1,
                               Map.Entry<Object, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });

        for (Map.Entry<Object, Integer> mapping : list) {
            webSocketHandlers.add((WebSocketHandler) mapping.getKey());
        }
    }

    /**
     * 执行异常处理方法
     *
     * @param throwableClass 异常类型
     * @return 异常处理方法结果
     */
    public static Object execExceptionHandler(Class<?> throwableClass) {
        //精准匹配
        Map<Method, Object> methodObjectMap = exceptionHandlers.get(throwableClass.getName());
        if (methodObjectMap != null) {
            for (Map.Entry<Method, Object> methodObjectEntry : methodObjectMap.entrySet()) {
                Method method = methodObjectEntry.getKey();
                try {
                    return method.invoke(methodObjectEntry.getValue());
                } catch (IllegalAccessException | InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        //子类匹配
        for (Map.Entry<String, Map<Method, Object>> entry : exceptionHandlers.entrySet()) {
            String k = entry.getKey();
            Map<Method, Object> v = entry.getValue();
            try {
                Class<?> aClass = Class.forName(k);
                boolean assignableFrom = aClass.isAssignableFrom(throwableClass);
                if (!assignableFrom) {
                    continue;
                }
                for (Map.Entry<Method, Object> mapEntry : v.entrySet()) {
                    Method k1 = mapEntry.getKey();
                    Object v1 = mapEntry.getValue();
                    try {
                        return k1.invoke(v1);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        throw new RuntimeException(e);
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    /**
     * 执行前置中间件
     *
     * @param nettyHttpRequest 前置上下文
     */
    public static void execBefore(NettyHttpRequest nettyHttpRequest) {
        middlewareActuator.execBefore(nettyHttpRequest);
    }


    /**
     * 执行返回值包装器
     *
     * @param result 待包装结果
     */
    public static Object execResponseDevice(Object result) {
        if (null != responseDevice) {
            return responseDevice.beforeBodyWrite(result);
        }
        return result;
    }

    /**
     * 获取 websocket处理器 集合
     *
     * @return
     */
    public static List<WebSocketHandler> getWebSocketHandlers() {
        return webSocketHandlers;
    }
}